package com.opener.web.auth.service.impl;

import com.opener.common.encrypt.AesUtils;
import com.opener.common.utils.Radix64;
import com.opener.web.auth.model.UserAuthToken;
import org.apache.commons.codec.digest.DigestUtils;

import java.util.Date;
import java.util.Random;

public class UKeyUtils {
    private static int SEGMENT_FLAG_LEN = 3;
    private static int SALT_BITS = 8;
    private static int CHECK_CODE_BITS = 4;
    private final static String UKEY_VER_AA = "AA";
    private final static String UKEY_VER_AB = "AB";

    public static String formatLen(int n, int len, int radix){
        String tmp = "0000000000"+Integer.toString(n, Character.MAX_RADIX);
        String ret = tmp.substring(tmp.length() - len);
        return ret;
    }
    private static String toString(long n,int radix){
        if (radix == 64){
            return Radix64.toString(n);
        }
        return Long.toString(n, radix);
    }
    public static Integer parseInteger(String s, int radix){
        Integer n = 0;
        if (radix == 64){
            n = (int)Radix64.parseLong(s);
            return n;
        }
        n = Integer.parseInt(s, Character.MAX_RADIX);
        return n;
    }
    public static Long parseLong(String s, int radix){
        Long n = 0L;
        if (radix == 64){
            n = Radix64.parseLong(s);
            return n;
        }
        n = Long.parseLong(s, Character.MAX_RADIX);
        return n;
    }
    public static String createKeyAA(UserAuthToken token){
        /**
         *  prefix|uid length|uid|device id length|device id|expire time length|expire time|ip length|ip|media time length|media time|salt 8 bits|checkcode 4bits
         */
        StringBuffer sb = new StringBuffer();
        // 加入uid
        String segment = toString( token.getUid(), Character.MAX_RADIX);
        String segmentLen = formatLen(segment.length(), SEGMENT_FLAG_LEN, Character.MAX_RADIX);
        sb.append(segmentLen);
        sb.append(segment);
        //加入device id
        segment = token.getDeviceId() == null ? "" : DigestUtils.sha256Hex(String.valueOf(token.getDeviceId()));
        segmentLen = formatLen(segment.length(), SEGMENT_FLAG_LEN, Character.MAX_RADIX);
        sb.append(segmentLen);
        sb.append(segment);
        //加入 expire time
        segment = String.valueOf(token.getExpireTime().getTime());
        segmentLen = formatLen(segment.length(), SEGMENT_FLAG_LEN, Character.MAX_RADIX);
        sb.append(segmentLen);
        sb.append(segment);
        //加入 ip
        segment = token.getIp() == null ? "": String.valueOf(token.getIp());
        segmentLen = formatLen(segment.length(), SEGMENT_FLAG_LEN, Character.MAX_RADIX);
        sb.append(segmentLen);
        sb.append(segment);
        //加入create time
        segment = String.valueOf(token.getCreateTime().getTime());
        segmentLen = formatLen(segment.length(), SEGMENT_FLAG_LEN, Character.MAX_RADIX);
        sb.append(segmentLen);
        sb.append(segment);
        //加入random 随机数8位
        Random r = new Random();
        for(int i = 0; i<SALT_BITS;i++){
            sb.append(r.nextInt(10));
        }
        //加入校验位 4位
        String shaString = DigestUtils.sha256Hex(sb.toString());
        String checkCode = shaString.substring(shaString.length()-CHECK_CODE_BITS);
        sb.append(checkCode);
        String ukey = UKEY_VER_AA + AesUtils.encryptString(sb.toString(),checkCode)+checkCode;
        return ukey;
    }
    public static UserAuthToken parseKeyAA(String ukey) {
        /**
         * prefix|uid length|uid|device id length|device id|expire time length|expire time|ip length|ip|media time length|media time|salt 8 bits|checkcode 4bits
         */
        try {
            if (ukey.startsWith(UKEY_VER_AA)){
                String checkCode = ukey.substring(ukey.length()-CHECK_CODE_BITS);
                String encryptString = ukey.substring(UKEY_VER_AA.length(),ukey.length()-checkCode.length());
                String s = AesUtils.decryptString(encryptString, checkCode);
                UserAuthToken token = new UserAuthToken();

                int segmentLen = SEGMENT_FLAG_LEN;
                int pos = 0;
                // parse uid

                int len = parseInteger(s.substring(pos,pos+segmentLen), Character.MAX_RADIX);
                pos += segmentLen;
                String segment = s.substring(pos,pos+len);
                token.setUid(parseLong(segment, Character.MAX_RADIX));
                pos += len;

                // parse device id
                len = parseInteger(s.substring(pos,pos+segmentLen), Character.MAX_RADIX);
                pos += segmentLen;
                segment = s.substring(pos,pos+len);
                token.setDeviceId(segment);
                pos += len;

                // parse expire time
                len = parseInteger(s.substring(pos,pos+segmentLen), Character.MAX_RADIX);
                pos += segmentLen;
                segment = s.substring(pos,pos+len);
                Date expireTime = new Date(Long.parseLong(segment));
                token.setExpireTime(expireTime);
                pos += len;
                // parse ip
                len = parseInteger(s.substring(pos,pos+segmentLen), Character.MAX_RADIX);
                pos += segmentLen;
                segment = s.substring(pos,pos+len);
                token.setIp(segment);
                pos += len;
                // parse media time
                len = parseInteger(s.substring(pos,pos+segmentLen), Character.MAX_RADIX);
                pos += segmentLen;
                segment = s.substring(pos,pos+len);
                Date createTime = new Date(Long.parseLong(segment));
                token.setCreateTime(createTime);
                pos += len;
                // parse salt
                len = SALT_BITS;
                String salt = s.substring(pos,pos+len);
                pos += len;
                // parse checkcode
                len = CHECK_CODE_BITS;
                String checkCode2  = s.substring(pos,pos+len);
                if(checkCode.equals( checkCode) ){
                    return token;
                }

            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    public static String createKeyAB(UserAuthToken token){
        /**
         * prefix|uid length|uid|device id length|device id|create time length|create time|expire time length|expire time|rnd|checkcode 4bits
         */
        int DEF_RADIX = 64;
        int FLAG_RADIX = Character.MAX_RADIX;

        StringBuffer sb = new StringBuffer();
        // 加入uid
        String segment = toString(token.getUid(), DEF_RADIX);
        String segmentLen = formatLen(segment.length(), SEGMENT_FLAG_LEN, FLAG_RADIX);
        sb.append(segmentLen);
        sb.append(segment);
        //加入device id
        segment = token.getDeviceId() == null ? "" : DigestUtils.sha1Hex(String.valueOf(token.getDeviceId()));
        segmentLen = formatLen(segment.length(), SEGMENT_FLAG_LEN,FLAG_RADIX);
        sb.append(segmentLen);
        sb.append(segment);
        //加入create time
        segment = toString(token.getCreateTime().getTime(), DEF_RADIX);
        segmentLen = formatLen(segment.length(), SEGMENT_FLAG_LEN,FLAG_RADIX);
        sb.append(segmentLen);
        sb.append(segment);

        //加入 expire time
        segment = toString(token.getExpireTime().getTime(), DEF_RADIX);
        segmentLen = formatLen(segment.length(), SEGMENT_FLAG_LEN,FLAG_RADIX);
        sb.append(segmentLen);
        sb.append(segment);

        //加入random 随机数8位
        Random r = new Random();
        long rnd = 0;
        for(int i = 0; i<SALT_BITS;i++){
            int j = r.nextInt(10);
            rnd = rnd*10+j;
        }
        String rndStr = toString(rnd, DEF_RADIX);
        sb.append(rndStr);
        System.out.println("salt:"+rndStr);
        //加入校验位 4位
        String shaString = DigestUtils.sha1Hex(sb.toString());
        String checkCode = shaString.substring(shaString.length()-CHECK_CODE_BITS);
        sb.append(checkCode);
        System.out.println("checkCode:"+checkCode);
        String ukey = UKEY_VER_AB + sb.toString();
        return ukey;
    }
    /**
     * prefix|uid length|uid|device id length|device id|create time length|create time|expire time length|expire time|rnd|checkcode 4bits
     */
    public static UserAuthToken parseKeyAB(String ukey){
        UserAuthToken token = null;
        if (ukey.startsWith(UKEY_VER_AB)){
            token = new UserAuthToken();
            String s = ukey;
            int DEF_RADIX = 64;
            int FLAG_RADIX = Character.MAX_RADIX;
            int segmentLen = SEGMENT_FLAG_LEN;
            int pos = UKEY_VER_AB.length();
            // parse uid
            int len = parseInteger(s.substring(pos,pos+segmentLen), FLAG_RADIX);
            pos += segmentLen;
            String segment = s.substring(pos,pos+len);
            long uid = parseLong(segment, DEF_RADIX);
            token.setUid(uid);
            pos += len;

            // parse device id
            len = parseInteger(s.substring(pos,pos+segmentLen), FLAG_RADIX);
            pos += segmentLen;
            segment = s.substring(pos,pos+len);
            token.setDeviceId(segment);
            pos += len;

            // parse create time
            len = parseInteger(s.substring(pos,pos+segmentLen), FLAG_RADIX);
            pos += segmentLen;
            segment = s.substring(pos,pos+len);
            Date createTime = new Date(Radix64.parseLong(segment));
            token.setCreateTime(createTime);
            pos += len;
            // parse expire time
            len = parseInteger(s.substring(pos,pos+segmentLen), FLAG_RADIX);
            pos += segmentLen;
            segment = s.substring(pos,pos+len);
            Date expireTime = new Date(Radix64.parseLong(segment));
            token.setExpireTime(expireTime);
            pos += len;

            // parse salt
            len = CHECK_CODE_BITS;
            String salt = s.substring(pos,s.length()-len);
            System.out.println("AB salt:"+salt);
            // parse checkcode
            len = CHECK_CODE_BITS;
            String checkCode = s.substring(s.length()-len);
            System.out.println("AB ccode:"+checkCode);


        }
        return token;
    }
    public static UserAuthToken parse(String ukey){
        String ver = ukey.substring(0,2);
        UserAuthToken token = null;
        switch (ver){
            case UKEY_VER_AA:{
                token = parseKeyAA(ukey);
                break;
            }
            case UKEY_VER_AB:{
                token = parseKeyAB(ukey);
            }
        }
        return token;
    }
    public static void main(String[] args){
        UserAuthToken token = new UserAuthToken();
        token.setUid(222222222L);
        token.setDeviceId("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36");
        token.setCreateTime(new Date());
        token.setExpireTime(new Date());
        token.setIp("192.168.10.103");
        String s = createKeyAA(token);
        System.out.println("AA:"+s);
        UserAuthToken t = parse(s);
        System.out.println("parseAA:"+t);
//        if (true)
//            return;


        s = createKeyAB(token);
        System.out.println("AB:"+s.length()+","+s);
        t = parse(s);
        System.out.println("parseAB:"+t);
    }
}
